/* Copyright ©, 2022, ECARX (Hubei) Technology Co., Ltd. All rights reserved.
 *
 * NOTICE: This file contains information which is confidential and proprietary to ECARX
 * (Hubei) Technology Co., Ltd. and/or other developers. No license to any intellectual
 * property rights contained within this file is either granted or implied except as may be
 * provided in an agreement with ECARX (Hubei) Technology Co., Ltd.
 *
 * Any unauthorized copying or distribution of content from this file in whole or in part
 * is prohibited.
 */

#include "vhal_core/property/IProperty.h"

#include <utils/SystemClock.h>

#include "../PropValueStore.h"
#include "VHalLog.h"

extern ecarx::vhal::core::property::storage::PropValueStore mStorage;
namespace ecarx::vhal::core::property {

IProperty::IProperty(const int32_t propertyId, const char* propName,
                     VehiclePropertyChangeMode chanMode, VehiclePropertyAccess access)
    : mPropertyId(propertyId), mPropName(propName) {
    config.prop = propertyId;
    config.changeMode = chanMode;
    config.access = access;
    config.configString = propName;
}
IProperty::~IProperty() {}

void IProperty::sampleRate(const float minSampleRate, const float maxSampleRate) {
    if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) {
        config.minSampleRate = minSampleRate;
        config.maxSampleRate = maxSampleRate;
    }
}

void IProperty::mixedTypeConfig(bool oneStr, bool oneBool, bool oneInt, int32_t sizeInt,
                                bool oneLong, int32_t sizeLong, bool oneFloat, int32_t sizeFloat,
                                int32_t sizeByte) {
    if (getPropType(mPropertyId) == VehiclePropertyType::MIXED) {
        config.configArray = {oneStr,   oneBool,  oneInt,    sizeInt, oneLong,
                              sizeLong, oneFloat, sizeFloat, sizeByte};
    }
}

void IProperty::configArray(std::vector<int32_t>& array) {
    config.configArray = array;
}

void IProperty::executeTimer(uint32_t msgId) const {
    auto it = timerTasks.find(msgId);
    if (it != timerTasks.end()) {
        mStorage.beginTransaction();
        it->second();
        mStorage.endTransaction();
    }
}

void IProperty::addArea(Area* area) {
    mAreas.try_emplace(area->getAreaId(), area);
}

Area* IProperty::getArea(int32_t areaId) const {
    auto it = mAreas.find(areaId);
    if (it != mAreas.end()) {
        return it->second;
    }
    return nullptr;
}

bool IProperty::invalidProperty() {
    for (auto it=mAreas.begin();it!=mAreas.end();){
        auto status = it->second->exeStatusFn();
        if(status == PropertyStatus::NotAvailable){
            delete it->second;
            mAreas.erase(it++);
        }else{
            it++;
        }
    }
    return mAreas.empty();
}

WorkPhase IProperty::getWorkPhase() {
    return mWorkPhase;
}

void IProperty::setWorkPhase(WorkPhase phase) {
    mWorkPhase = phase;
}

bool IProperty::setFunctionValue(const VehiclePropValue& value) {
    auto iter = mAreas.find(value.areaId);
    if (iter != mAreas.end()) {
        return iter->second->exeSetFunction(value);
    }
    VHALOG.w("setFunctionValue propId:0x%x areaId:%d not found.", value.prop, value.areaId);
    return false;
}

void IProperty::registerWritePropValueListener(std::function<void(const VehiclePropValue& value,CommitMode mode)>&& fn) {
    mReportValueCallback = fn;
}

void IProperty::writeToStore(const VehiclePropValue& value,CommitMode mode) const {
    if (mReportValueCallback != nullptr) {
        mReportValueCallback(value,mode);
    }
}

void IProperty::setDID(persist::DID* did) {
    mDID = did;
}
persist::DID* IProperty::getDID() {
    return mDID;
}
void IProperty::setPlatformProfile(persist::PlatformProfile* profile) {
    mPlatformProfile = profile;
}
persist::PlatformProfile* IProperty::getPlatformProfile() {
    return mPlatformProfile;
}

Area::Area(IProperty* property, int32_t areaId, VehiclePropertyStatus status)
    : mProperty(property), areaId(areaId), key(property->getName() + "::" + std::to_string(areaId)) {
    auto propName = property->getName();
    // 初始设置
    mStatus = PropertyStatus(status);
    mPropValue.prop = property->getPropertyId();
    mPropValue.areaId = areaId;
    mPropValue.status = status;

    areaConfig.areaId = areaId;
    areaConfig.minInt32Value = 0;
    areaConfig.maxInt32Value = 0;
    areaConfig.minInt64Value = 0;
    areaConfig.maxInt64Value = 0;
    areaConfig.minFloatValue = 0;
    areaConfig.maxFloatValue = 0;

    property->addArea(this);
}

PropertyStatus Area::exeSignalChanged(int32_t signalId, int64_t token, int64_t timestamp) {
    auto iter = mSignalCallback.find(signalId);
    if (iter != mSignalCallback.end()) {
        for (auto& _callback : iter->second) {
            _callback(token, timestamp, this);
        }
    }
    return mStatus;
}

bool Area::exeSetFunction(const VehiclePropValue& value) {
    if (mAreaSetFn && (mStatus == PropertyStatus::Active)) {
        mOnSetTimestamp = android::elapsedRealtimeNano();
        mAreaSetFn(value);
        return true;
    }
    VHALOG.w("exeSetFunction propId:0x%x areaId:%d status:%d maybe not active.",
        value.prop, value.areaId, static_cast<int32_t>(mStatus));
    return false;
}

PropertyStatus Area::exeStatusFn() {
    if (mStatusFn != nullptr) {
        updateStatus(mStatusFn(this));
    }
    return mStatus;
}

/**
 * @brief 恢复任务,在ProfileChanged的时候触发
 * 有的时候由于在调用OnSetProperty的时候因为状态不可用则恢复失败,应该在状态可用的时候还会导致触发此函数
 * @param force 如果true表示强制恢复,否则没有只有没有恢复的情况下才会恢复
 * @return true 表示该PropertyValue发生了变化
 * @return false PropertyValue没有发生变化
 */
bool Area::tryRecoveryTask(bool force) {
    if (force || !isRecovery) {
        if (mRecoveryFn) {
            mRecoveryFn(this);
        }
        return true;
    }
    return false;
}

void Area::registerSignalCallback(int32_t signal, SignalChangedCallback&& callback) {
    this->signals.emplace(signal);
    auto iter = mSignalCallback.find(signal);
    if (iter != mSignalCallback.end()) {
        iter->second.emplace_back(SignalCallback{.mToken = 0, ._callback = callback});
    } else {
        mSignalCallback.try_emplace(
            signal, std::vector<SignalCallback>{SignalCallback{.mToken = 0, ._callback = callback}});
    }
}

void Area::registerPropValueChangeCallback(int32_t propId, int32_t areaId, PropValueChangeFn&& fn) {
    areaId = isGlobalProp(propId) ? 0 : areaId;
    if (propId == mProperty->getPropertyId() && areaId == getAreaId()) {
        return;
    }
    RecordId recId = {.prop = propId, .area = areaId};
    mPropChangeCallback.insert_or_assign(recId, fn);
}

void Area::setInitValueFn(InitValueFn&& fn) {
    fn(this);
}

void Area::setRecoveryFn(RecoveryValueFn&& fn) {
    this->mRecoveryFn = fn;
}

void Area::setStatusFn(StatusFn&& _callback) {
    this->mStatusFn = _callback;
}

bool Area::runnableBySignal(int64_t timestamp) {
    if (isSupportPersist()) {
        return mOnSetTimestamp > 0 && timestamp > mOnSetTimestamp;
    }
    return true;
}

void Area::recoveried(bool recoveried) {
    isRecovery = recoveried;
    if(isRecovery) {
        mOnSetTimestamp = android::elapsedRealtimeNano();
    }
}

persist::Properties* Area::getPersist() {
    if (mPersistType == PersistType::DID) {
        return getIProperty()->getDID();
    } else if (mPersistType == PersistType::Profile) {
        auto platformProfile = getIProperty()->getPlatformProfile();
        if (platformProfile != nullptr) {
            return platformProfile->getCurrentProfile();
        }
    }
    // 添加偏好设置存储
    else if (mPersistType == PersistType::Preference) {
        auto platformProfile = getIProperty()->getPlatformProfile();
        if (platformProfile != nullptr) {
            auto prifle = platformProfile->getCurrentProfile();
            if (prifle != nullptr) {
                return prifle->getCurrentPreference();
            }
        }
    }
    return nullptr;
}

void Area::updateStatus(PropertyStatus status) {
    mStatus = status;
    if (status != PropertyStatus::NotAvailable) {
        mPropValue.status = VehiclePropertyStatus(status);
    } else {
        mPropValue.status = VehiclePropertyStatus::UNAVAILABLE;
    }
    mPropValue.timestamp = android::elapsedRealtimeNano();
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }

    // 如果没有恢复则需要恢复该操作会在内部检查是否已经恢复了
    if (mProperty->getWorkPhase() == WorkPhase::READY_PHASE && status == PropertyStatus::Active) {
        tryRecoveryTask(false);
    }
}

const std::string& Area::getKey() const {
    return key;
}

void Area::setBoolValue(bool value, bool persist) {
    if (mPropValue.value.int32Values.size() == 0) {
        mPropValue.value.int32Values = {value};
    } else {
        mPropValue.value.int32Values[0] = value;
    }
    mPropValue.timestamp = android::elapsedRealtimeNano();
    if (persist) {
        auto persist = getPersist();
        if (persist != nullptr) {
            persist->setBool(getKey(), value);
        }
    }
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }
}

void Area::setInt32Value(int32_t value, bool persist) {
    if (mPropValue.value.int32Values.size() == 0) {
        mPropValue.value.int32Values = {value};
    } else {
        mPropValue.value.int32Values[0] = value;
    }
    mPropValue.timestamp = android::elapsedRealtimeNano();
    if (persist) {
        auto persist = getPersist();
        if (persist != nullptr) {
            persist->setInt32(getKey(), value);
        }
    }
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }
}

void Area::setInt32Values(const std::vector<int32_t>& value, bool persist) {
    mPropValue.value.int32Values = value;
    mPropValue.timestamp = android::elapsedRealtimeNano();
    if (persist) {
        auto persist = getPersist();
        if (persist != nullptr) {
            persist->setInt32Array(getKey(), value);
        }
    }
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }
}

void Area::setInt64Value(int64_t value, bool persist) {
    if (mPropValue.value.int64Values.size() == 0) {
        mPropValue.value.int64Values = {value};
    } else {
        mPropValue.value.int64Values[0] = value;
    }
    mPropValue.timestamp = android::elapsedRealtimeNano();
    if (persist) {
        auto persist = getPersist();
        if (persist != nullptr) {
            persist->setInt64(getKey(), value);
        }
    }
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }
}

void Area::setInt64Values(const std::vector<int64_t>& value, bool persist) {
    mPropValue.value.int64Values = value;
    mPropValue.timestamp = android::elapsedRealtimeNano();
    if (persist) {
        auto persist = getPersist();
        if (persist != nullptr) {
            persist->setInt64Array(getKey(), value);
        }
    }
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }
}

void Area::setFloatValue(float value, bool persist) {
    if (mPropValue.value.floatValues.size() == 0) {
        mPropValue.value.floatValues = {value};
    } else {
        mPropValue.value.floatValues[0] = value;
    }
    mPropValue.timestamp = android::elapsedRealtimeNano();
    if (persist) {
        auto persist = getPersist();
        if (persist != nullptr) {
            persist->setFloat(getKey(), value);
        }
    }
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }
}

void Area::setFloatValues(const std::vector<float>& value, bool persist) {
    mPropValue.value.floatValues = value;
    mPropValue.timestamp = android::elapsedRealtimeNano();
    if (persist) {
        auto persist = getPersist();
        if (persist != nullptr) {
            persist->setFloatArray(getKey(), value);
        }
    }
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }
}

void Area::setStringValue(const std::string& value, bool persist) {
    mPropValue.value.stringValue = value;
    mPropValue.timestamp = android::elapsedRealtimeNano();
    if (persist) {
        auto persist = getPersist();
        if (persist != nullptr) {
            persist->setString(getKey(), value);
        }
    }
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }
}

void Area::setBytes(const std::vector<uint8_t>& value, bool persist) {
    mPropValue.value.bytes = value;
    mPropValue.timestamp = android::elapsedRealtimeNano();
    if (persist) {
        auto persist = getPersist();
        if (persist != nullptr) {
            persist->setBytes(getKey(), value);
        }
    }
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }
}

void Area::setMixedValue(const MixedValue& value, bool persist) {
    mPropValue.value = value;
    mPropValue.timestamp = android::elapsedRealtimeNano();
    if (persist) {
        auto persist = getPersist();
        if (persist != nullptr) {
            std::string int32key = std::string(getKey()).append("::int32s");
            std::string int64key = std::string(getKey()).append("::int64s");
            std::string floatkey = std::string(getKey()).append("::floats");
            std::string strkey = std::string(getKey()).append("::str");
            std::string byteskey = std::string(getKey()).append("::bytes");
            persist->setInt32Array(int32key, value.int32Values);
            persist->setInt64Array(int64key, value.int64Values);
            persist->setFloatArray(floatkey, value.floatValues);
            persist->setString(strkey, value.stringValue);
            persist->setBytes(byteskey, value.bytes);
        }
    }
    auto& cfg = mProperty->getConfig();
    if (cfg.changeMode != VehiclePropertyChangeMode::STATIC ||
        mPropValue.status == VehiclePropertyStatus::AVAILABLE) {
        mProperty->writeToStore(mPropValue,mCommitMode);
    }
}

}  // namespace ecarx::vhal::core::property